package robot;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.util.HashMap;
import java.util.Random;

import environnement.Environnement;

import robot.io.capteurs.ICapteur;
import robot.io.capteurs.PresenceDevant;
import robot.io.infos.IInfos;
import robot.io.infos.VitesseMoyenne;
import robot.io.sorties.Acceleration;
import robot.io.sorties.ISortie;
import robot.io.sorties.Rotation;

/**
 * Class RobotCourse
 * Robot ayant pour but la vitesse moyenne la plus rapide
 */
public class RobotCourse implements IRobot
{

    //
    // Fields
    //
    /**
     * absisse
     */
    private int x;
    /**
     * ordonné
     */
    private int y;
    /**
     * Matrice des poids
     */
    private double[][] matricePoids;
    /**
     * Liste des informations supplémentaires
     */
    private IInfos[] listeInfos;
    /**
     * Liste des capteurs
     */
    private ICapteur[] listeCapteurs;
    /**
     * Liste des sorties
     */
    private ISortie[] listeSorties;
    /**
     * Informations necessaires aux interactions entres actions
     */
    private HashMap<String, Integer> ioRobot;
    /**
     * Valeurs des neurones
     */
    private double[] valeursNeurones;
    /**
     * Score en 0 et 1
     */
    private double score;

    /**
     * Objet dessin du robot
     */
    private Rectangle2D.Double robot;

    /**
     * Objet Environnement (plateau de jeu)
     */
    private Environnement env;

    //
    // Constructors
    //
    /**
     *
     * @param x
     * @param y
     * @param listeInfos
     * @param listeCapteurs
     * @param listeSorties
     * @param score
     */
    public RobotCourse(int x, int y, int score)
    {
    	// Initialisation du générateur de nombre aléatoire
    	Random r = new Random( System.nanoTime() );
    	
        // Initialisation des variables
        this.x = x;
        this.y = y;
        this.score = score;
        this.ioRobot = new HashMap<String, Integer>();
        this.env = Environnement.getInstance();
        
        
        //////////////////////////////////////////////////////
        //        Initialisation des entrees/sorties        //
        //              et info du robot                    //
        //////////////////////////////////////////////////////
        // Entrées
        this.listeCapteurs = new ICapteur[1];
        this.listeCapteurs[0] = new PresenceDevant();

        //Sorties
        this.listeSorties = new ISortie[2];
        this.listeSorties[0] = new Rotation();
        this.listeSorties[1] = new Acceleration();

        //Infos
        this.listeInfos = new IInfos[1];
        this.listeInfos[0] = new VitesseMoyenne() ;

        //////////////////////////////////////////////////////
        //         Referencement des ID des entrée / sorties//
        //////////////////////////////////////////////////////
        // Referencement des sorties
        for (int index = 0; index < this.listeSorties.length; ++index)
        {
            this.ioRobot.put(this.listeSorties[index].getClass().getName(), index);
        }

        // referencement des entrees
        for (int index = 0; index < this.listeCapteurs.length; ++index)
        {
            this.ioRobot.put(this.listeCapteurs[index].getClass().getName(), index);
        }
        
        // Referencement des infos
        for (int index = 0; index < this.listeInfos.length; ++index)
        {
            this.ioRobot.put(this.listeInfos[index].getClass().getName(), index);
        }

        //////////////////////////////////////////////////////
        //         Initialisation pour l'IA du robot        //
        //////////////////////////////////////////////////////
        this.matricePoids = new double[13][13];

        for (int i = 0; i < 13; ++i)
        {
            for (int j = 0; j < 13; ++j)
            {
                this.matricePoids[i][j] = r.nextDouble() ;
            }
        }

        this.valeursNeurones = new double[13];

        for (int i = 0; i < 13; ++i)
        {
            this.valeursNeurones[i] = r.nextDouble() ;
        }

    }

    //
    // Methods
    //
    //
    // Accessor methods
    //
    /**
     * Modifie la valeur de l'absisse
     */
    @Override
    public void setX(int x)
    {
        this.x = x;
    }

    /**
     * retourne la valeur de l'absisse
     */
    @Override
    public int getX()
    {
        return x;
    }

    /**
     * Modifie la valeur de l'ordonnée
     */
    @Override
    public void setY(int y)
    {
        this.y = y;

    }

    /**
     *
     */
    @Override
    public int getY()
    {
        return y;
    }

    /**
     * Retourne la variable correspondant à la clé
     */
    @Override
    public int getIndex(String key)
    {
        int var = 0;
        if (this.ioRobot.containsKey(key))
        {
            var = this.ioRobot.get(key);
        }
        else
        {
            var = 0;
        }
        return var;
    }

    @Override
    public void setIndex(String key, int valeur)
    {
        this.ioRobot.put(key, valeur);
    }

    @Override
    public void setMatricePoids(double[][] matrice)
    {
        this.matricePoids = matrice;
    }

    @Override
    public double[][] getMatricePoids()
    {
        return this.matricePoids;
    }

    @Override
    public void setListeInfos(IInfos[] infos)
    {
        this.listeInfos = infos;
    }

    @Override
    public IInfos[] getListeInfos()
    {
        return this.listeInfos;
    }

    @Override
    public void setListeCapteurs(ICapteur[] capteurs)
    {
        this.listeCapteurs = capteurs;
    }

    @Override
    public ICapteur[] getListeCapteurs()
    {
        return listeCapteurs;
    }

    @Override
    public void setListe_Sorties(ISortie[] sorties)
    {
        this.listeSorties = sorties;
    }

    @Override
    public ISortie[] getListe_Sorties()
    {
        return listeSorties;
    }

    @Override
    public void setScore(double score)
    {
        this.score = score;
    }

    @Override
    public double getScore()
    {
        return this.score;
    }

    //
    // Other methods
    //
    /**
     * Calcul le score suivant la vitesse moyenne du robot
     * @return       int
     * @param        robot Robot
     */
    public double calculScore()
    {
        return (double) this.listeInfos[ this.getIndex(VitesseMoyenne.class.getName()) ].getValeur() / 100;
    }

    /**
     * dessine le robot
     */
    public void dessiner(Graphics2D gr)
    {
        this.robot = new Rectangle2D.Double(this.env.getX() + x - 1, this.env.getY() + y - 1, 3, 3);
        gr.setPaint(Color.BLACK);
        gr.fill(this.robot);
    }

    public void executeActions(IRobot robot)
    {
        int nb = this.listeSorties.length;
        for (int act = 0; act < nb; ++act)
        {
            this.listeSorties[act].action(robot);
        }
    }

    public void updateCapteurs(IRobot robot)
    {
        int nb = this.listeCapteurs.length;
        for (int capt = 0; capt < nb; ++capt)
        {
            this.listeCapteurs[capt].miseAJourCapteur(robot);
        }
    }

    @Override
    public void updateInfos(IRobot robot)
    {
        int nb = this.listeInfos.length;
        for (int inf = 0; inf < nb; ++inf)
        {
            this.listeInfos[inf].miseAJourInfo(robot);
        }
    }

    @Override
    public double[] getValeursNeurones()
    {
        return this.valeursNeurones;
    }

    @Override
    public void setValeursNeurones(double[] valeurs)
    {
        this.valeursNeurones = valeurs;
    }

    /**
     * Permet de savoir si un point est dans le robot
     * @param x
     * @param y
     * @return Vrai quand dedans sinon faux
     */
    public boolean isIn(int x, int y)
    {
        return this.robot.contains(this.env.getX() + x, this.env.getY() + y);
    }
}
